package com.eastfair.util;



import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.util.Random;

public class SnowflakeUtils {
    //开始时间（这里使用2019年4月1日整点）
    private final static long START_TIME = 1554048000000L;
    //数据中心编号所占数位
    private final static long DATA_CENTER_BITS = 10L;
    //最大数据中心编号
    private final static long MAX_DATA_CENTER_ID = 1023;
    //序列编号占位位数
    private final static long SEQUENCE_BIT = 12L;
    //数据中心编号向左移12位
    private final static long DATA_CENTER_SHIFT = SEQUENCE_BIT;
    //时间戳向左移22位（10+12）
    private final static long TIMESTAMP_SHIFT = DATA_CENTER_BITS+DATA_CENTER_SHIFT;

    //最大生成序列号，这里为4095
    private final static long MAX_SEQUENCE = 4095;
    //数据中心ID（0~1023）
    private long dataCenterId;
    //毫秒内序列（0~4095）
    private long sequence = 0L;
    //上次生成ID的时间戳
    private long lastTimestamp = -1L;

    /*
    现在的微服务和分布式趋于中心化，所以不需要受理机器编号
    10位二进制全部用于数据中心
     */
    public SnowflakeUtils(long dataCenterId){
        if (dataCenterId>MAX_DATA_CENTER_ID){
            String msg = "数据中心编号["+dataCenterId+"]超过最大允许值["+MAX_DATA_CENTER_ID+"]";
            throw new RuntimeException(msg);
        }

        if (dataCenterId<0){
            String msg = "数据中心编号["+dataCenterId+"]不允许小于0";
            throw new RuntimeException(msg);
        }
        this.dataCenterId = dataCenterId ;
    }


    /*
    获得下一个ID
     */
    public synchronized long nextId(){
        //获取当前时间
        long timestamp = System.currentTimeMillis();
        //如果是同一个毫秒时间戳的处理
        if(timestamp == lastTimestamp){
            sequence+=1;//序号+1
            if(sequence>MAX_SEQUENCE){
                sequence = 0;
                //等待到下一毫秒
                timestamp = tilNextMillis(timestamp);
            }
        }else {
            //修改时间戳
            lastTimestamp = timestamp ;
            //序列号重新开始
            sequence = 0;
        }
        //二进制的位运算，其中“<<”代表左移
        long result = ((timestamp-START_TIME)<<TIMESTAMP_SHIFT)
                |(this.dataCenterId<<DATA_CENTER_SHIFT)
                |sequence;
        return result;
    }

    /*
    阻塞到下一毫秒，直到获得新的时间戳
     */
    protected long tilNextMillis(long lastTimestamp){
        long timestamp;
        do{
            timestamp = System.currentTimeMillis();
        }while (timestamp>lastTimestamp);
        return timestamp;
    }

}
